package com.hzgj.soa.spring.corr;

import com.hzgj.bcl.soa.config.ServerDef;
import com.hzgj.bcl.soa.stats.StatsCollectors;
import com.hzgj.bcl.soa.util.GlobalInvoke;
import com.hzgj.bcl.util.jmx.Management;
import com.hzgj.soa.spring.Domain;
import com.hzgj.soa.spring.ReferenceConfig;
import com.hzgj.soa.spring.ServerBean;
import com.hzgj.soa.spring.ServiceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.AbstractApplicationContext;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.hzgj.soa.spring.schema.SoaNamespaceHandler.DOMAINCONFIGBEAN_NAME;

public class CorrListener implements ApplicationListener<ContextRefreshedEvent> {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private AtomicBoolean start = new AtomicBoolean(false);

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        if (start.compareAndSet(false, true)) {
            boolean server = isServer(applicationContext);
            logger.info("register mbean count = " + Management.getMBeanServer().getMBeanCount());
            startMbeanServer();
            DefaultListableBeanFactory beanFactory = getDefaultListableBeanFactory(applicationContext);
            SpringConfigCheckProcessor springConfigCheckProcessor = new SpringConfigCheckProcessor();
            springConfigCheckProcessor.process(beanFactory);
            removeConfig(applicationContext, beanFactory);
            if (server) {
                try {
                    Map<String, ServerDef> servers = applicationContext.getBeansOfType(ServerDef.class);
                    for (String s : servers.keySet()) {
                        GlobalInvoke.addContext(new GlobalInvoke.ServerStartedInvokeEvent(s), this, CorrListener.class.getMethod("removeServerConfig",
                                ApplicationContext.class, DefaultListableBeanFactory.class, String.class), new Object[]{applicationContext, beanFactory, s});
                    }
                } catch (Exception e) {
                    logger.error("{}", e);
                }
                StatsCollectors.getInstance().load();
                applicationContext.getBean(ServerBean.class).start();
            }
        }
    }

    private boolean isServer(ApplicationContext applicationContext) {
        return applicationContext.containsBean(ServerBean.class.getSimpleName());
    }

    private void startMbeanServer() {
        if (System.getProperty("enable.jmx", "true").equals("true")) {
            try {
                Management.ConnectorServer.start();
            } catch (IOException e) {
                logger.error("{}", e);
            }
        }
    }

    public void removeServerConfig(ApplicationContext applicationContext, DefaultListableBeanFactory defaultListableBeanFactory, String serverId) {

        ServerDef serverDef = applicationContext.getBean(serverId, ServerDef.class);
        if (serverDef == null) {
            logger.error("can't find server with id " + serverId);
        }

        serverDef.getServiceDefs().forEach(attr -> {
            if (logger.isDebugEnabled()) {
                logger.debug("remove BeanDefinition " + attr.getId());
            }
            defaultListableBeanFactory.removeBeanDefinition(attr.getId());
        });

        defaultListableBeanFactory.removeBeanDefinition(serverId);
        if (serverDef.isSuspend()) {
            GlobalInvoke.clear();
        }
    }

//    private void removeReference(ApplicationContext applicationContext, DefaultListableBeanFactory defaultListableBeanFactory) {
//        Map<String, ReferenceDef> referenceDefs = applicationContext.getBeansOfType(ReferenceDef.class);
//        referenceDefs.keySet().forEach(name -> {
//            if (logger.isDebugEnabled()) {
//                logger.debug("remove BeanDefinition " + name);
//            }
//            defaultListableBeanFactory.removeBeanDefinition(name);
//        });
//        Map<String, ReferenceBean> beans = applicationContext.getBeansOfType(ReferenceBean.class);
//        beans.keySet().forEach(name -> {
//            if (logger.isDebugEnabled()) {
//                logger.debug("remove BeanDefinition " + name);
//            }
//            defaultListableBeanFactory.removeBeanDefinition(name);
//        });
//    }

    private void removeConfig(ApplicationContext applicationContext, DefaultListableBeanFactory defaultListableBeanFactory) {
        Map<String, ServiceConfig> serviceConfigs = applicationContext.getBeansOfType(ServiceConfig.class);
        serviceConfigs.keySet().forEach(name -> {
            if (logger.isDebugEnabled()) {
                logger.debug("remove BeanDefinition " + name);
            }
            defaultListableBeanFactory.removeBeanDefinition(name);
        });
        Map<String, ReferenceConfig> referenceConfigs = applicationContext.getBeansOfType(ReferenceConfig.class);
        referenceConfigs.keySet().forEach(name -> {
            if (logger.isDebugEnabled()) {
                logger.debug("remove BeanDefinition " + name);
            }
            defaultListableBeanFactory.removeBeanDefinition(name);
        });

        Map<String, Domain> domains = applicationContext.getBeansOfType(Domain.class);
        domains.keySet().forEach(name -> {
            if (logger.isDebugEnabled()) {
                logger.debug("remove BeanDefinition " + name);
            }
            defaultListableBeanFactory.removeBeanDefinition(name);
        });

        defaultListableBeanFactory.removeBeanDefinition(DOMAINCONFIGBEAN_NAME);
        if (logger.isDebugEnabled()) {
            logger.debug("remove BeanDefinition domainConfigBean");
        }
    }

    private DefaultListableBeanFactory getDefaultListableBeanFactory(ApplicationContext applicationContext) {
        ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory();
        DefaultListableBeanFactory defaultListableBeanFactory = null;
        if (beanFactory instanceof DefaultListableBeanFactory) {
            defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
        }
        return defaultListableBeanFactory;
    }
}